package com.benshou.bcss.common.db;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.interfaces.Compare;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.benshou.bcss.common.db.query.LambdaQueryApplicator;
import com.benshou.bcss.common.j17common.Pagination;
import com.benshou.bcss.common.j17common.annotation.NotNull;
import com.benshou.bcss.common.tenant.TenantContext;

import java.util.List;
import java.util.Objects;
import java.util.function.Function;

/**
 * 抽象类：数据访问对象。处理对象是 {@link BaseEntity} 及其子类
 *
 * @author <a href="mailto:liuzhenbang@benshou.com">Liu Zhenbang</a>
 */
public abstract class AbstractBaseEntityDAO<M extends BaseMapper<T>, T extends BaseEntity> extends BaseDAO<M, T> {

    /**
     * 生成更新方法，以用于 Mapper 的直接调用。
     *
     * @param applicator 查询参数的设置对象
     * @param updateModel 更新的原型
     * @return 更新的 Mapper 调用方法
     */
    protected Function<M, @NotNull Integer> update(
            @NotNull LambdaQueryApplicator<T> applicator,
            @NotNull T updateModel) {
        return mapper -> mapper.update(updateModel, toSelectWrapper(applicator));
    }

    /**
     * 生成逻辑删除方法，以用于 Mapper 的直接调用。
     *
     * @param applicator 查询参数的设置对象
     * @return 逻辑删除的 Mapper 调用方法
     */
    protected Function<M, @NotNull Integer> logicalDelete(@NotNull LambdaQueryApplicator<T> applicator) {
        return mapper -> mapper.update(applyCommonQueryCondition(applicator.applyQuery(lambdaUpdate()))
                .set(T::getIsDeleted, true)
                .getWrapper()
                .setEntityClass(Objects.requireNonNull(getEntityClass())));
    }

    /**
     * 生成获取第一个对象的方法，以用于 Mapper 的直接调用。
     *
     * @param applicator 查询参数的设置对象
     * @return 获取第一个对象的 Mapper 调用方法
     */
    protected Function<M, T> getOne(LambdaQueryApplicator<T> applicator) {
        return mapper -> mapper.selectOne(toSelectWrapper(applicator));
    }

    /**
     * 生成获取列表的方法，以用于 Mapper 的直接调用。
     *
     * @param applicator 查询参数的设置对象
     * @return 获取列表的 Mapper 调用方法
     */
    protected Function<M, List<T>> getList(LambdaQueryApplicator<T> applicator) {
        return mapper -> mapper.selectList(toSelectWrapper(applicator));
    }

    /**
     * 生成获取所有对象的方法，以用于 Mapper 的直接调用。
     *
     * @return 获取所有对象的 Mapper 调用方法
     */
    protected Function<M, List<T>> getAll() {
        return getList(null);
    }

    /**
     * 生成分页获取列表的方法，以用于 Mapper 的直接调用。
     *
     * @param pagination 分页参数
     * @param applicator 查询参数的设置对象
     * @return 分页获取列表的 Mapper 调用方法
     */
    protected Function<M, Page<T>> getPage(
            @NotNull Pagination pagination,
            LambdaQueryApplicator<T> applicator) {
        return mapper -> mapper.selectPage(
                generatePageHolder(pagination),
                toSelectWrapper(applicator));
    }

    /**
     * 生成查询对象是否存在的方法，以用于 Mapper 的直接调用。
     *
     * @param applicator 查询参数的设置对象
     * @return 查询对象是否存在的 Mapper 调用方法
     */
    protected Function<M, @NotNull Boolean> isExists(LambdaQueryApplicator<T> applicator) {
        return mapper -> mapper.exists(toSelectWrapper(applicator));
    }

    /**
     * 生成获取对象数量的方法，以用于 Mapper 的直接调用。
     *
     * @param applicator 查询参数的设置对象
     * @return 获取对象数量的 Mapper 调用方法
     */
    protected Function<M, @NotNull Long> getCount(LambdaQueryApplicator<T> applicator) {
        return mapper -> mapper.selectCount(toSelectWrapper(applicator));
    }

    /**
     * 生成分页结果承载对象
     *
     * @param pagination 分页参数
     * @return 分页结果承载对象
     */
    protected Page<T> generatePageHolder(@NotNull Pagination pagination) {
        return pagination.paginationDisabled() ?
                Page.of(1L, -1L) :
                Page.of(pagination.pageNo(), pagination.pageSize());
    }

    /**
     * 转为查询参数 Wrapper
     *
     * @param applicator 查询参数的设置对象
     * @return 查询参数 Wrapper
     */
    private AbstractWrapper<T, SFunction<T, ?>, LambdaQueryWrapper<T>> toSelectWrapper(
            LambdaQueryApplicator<T> applicator) {
        var query = lambdaQuery();
        if (applicator != null) {
            query = applicator.applyQuery(query);
        }

        return applyCommonQueryCondition(query).getWrapper();
    }

    /**
     * 应用通用的查询条件
     *
     * @param query 查询参数承载对象
     * @return 传入的查询参数承载对象
     * @param <Q> 查询参数承载对象的类型
     */
    private <Q extends Compare<Q, SFunction<T, ?>>> Q applyCommonQueryCondition(@NotNull Q query) {
        return query
                .eq(!TenantContext.isPublic(), T::getTenantId, BaseEntity.getCurrentTenantId())
                .eq(T::getIsDeleted, false);
    }
}
